import EventEmitter from '../eventEmitter';
import Stage from '../stage';
import { getImage } from '../image';
import {
  dist,
  getNodesMinEnclosingRect,
  getRealRect,
  getType,
  isPointInsideCircle,
  normalizeRotation,
  transformedCoord,
  vectorRotation,
  draw,
  midpoint,
  isNumber,
} from '@/utils';
import { onRotateStart, onRotate, onRotateEnd, onResizeStart, onResize, onResizeEnd } from './evnet';
import { pointsName, cPointsName, cursorArr } from './constant';

/**
 * 转换器类
 */
export default class Transformer extends EventEmitter {
  className = 'Transformer'; // 类名
  nodes = []; // 转换节点
  points = []; // 点集
  rotation = 0; // 转换器的旋转角度
  rotating = false; // 旋转标识
  resizing = false; // 调整大小标识
  anchorName = null; // 调整大小锚点名
  lastGridCoord = { x: 0, y: 0 }; // 记录上一次网格坐标
  constructor(stage) {
    super();
    this.stage = stage;
    // 初始化事件
    this.initEvents();
    // 初始化图标
    this.initIcon();
  }
  // 初始化事件
  initEvents() {
    // 旋转相关
    this.onRotateStart = onRotateStart.bind(this);
    this.onRotate = onRotate.bind(this);
    this.onRotateEnd = onRotateEnd.bind(this);
    // 调整大小相关
    this.onResizeStart = onResizeStart.bind(this);
    this.onResize = onResize.bind(this);
    this.onResizeEnd = onResizeEnd.bind(this);
  }
  // 初始化图标
  initIcon() {
    // 旋转图标
    this.rotateImage = getImage('https://front-xps-cdn.xsyx.xyz/custom/marvel-design-web/images/rotate.svg');
    // 深度操作节点图标
    this.depthImage = getImage('https://front-xps-cdn.xsyx.xyz/custom/marvel-design-web/images/detph.svg');
    // 深度操作节点图标(主题色)
    this.depthImageTheme = getImage('https://front-xps-cdn.xsyx.xyz/custom/marvel-design-web/images/detph-theme.svg');
    const images = [this.rotateImage, this.depthImage, this.depthImageTheme];
    let count = 0;
    const draw = () => this.visible && this.stage.draw();
    for (let image of images) {
      // 监听加载完毕
      image.onload = () => ++count && count === images.length && draw();
      // 图片加载出错
      image.onerror = () => draw();
    }
  }
  // 转换器中是否有带锚点的节点
  get nodesHasAnchors() {
    return this.nodes.some((node) => node.hasAnchors);
  }
  // 转换器是否可见
  get visible() {
    const { stage, nodes } = this;
    // 不可转换节点，不可见
    if (!stage.transformable) return false;
    // 转换器中没有节点，不可见
    if (!nodes.length) return false;
    return true;
  }
  // 调整尺寸控件是否可见
  get resizeVisible() {
    return !this.nodesHasAnchors;
  }
  // 旋转控件是否可见
  get rotateVisible() {
    return !this.nodesHasAnchors;
  }
  // 中心点坐标
  get center() {
    const totalX = this.points.reduce((total, { x }) => total + x, 0);
    const totalY = this.points.reduce((total, { y }) => total + y, 0);
    return { x: totalX / 4, y: totalY / 4 };
  }
  // 边集
  get edges() {
    return this.points.map((point, index, array) => ({ o: point, d: array[(index + 1) % array.length] }));
  }
  // 边中心点集
  get cPoints() {
    return this.edges.reduce((arr, edge) => {
      const { o, d } = edge;
      arr.push(midpoint(o, d));
      return arr;
    }, []);
  }
  // 实际宽度
  get width() {
    const { cPoints, center } = this;
    if (!cPoints.length) return 0;
    return 2 * dist(cPoints[1], center);
  }
  // 实际高度
  get height() {
    const { cPoints, center } = this;
    if (!cPoints.length) return 0;
    return 2 * dist(cPoints[2], center);
  }
  // 实际旋转角度
  get realRotation() {
    const { cPoints, center } = this;
    if (!cPoints.length) return 0;
    return vectorRotation(center, cPoints[1]);
  }
  // 实际展示的旋转角度
  get showRotation() {
    const { nodes, rotation } = this;
    return normalizeRotation(Math.round(nodes.length === 1 ? nodes[0].rotation : rotation));
  }
  // 旋转控件
  get rotateWidget() {
    const { center, realRotation, height } = this;
    const { x, y } = transformedCoord({ x: center.x, y: center.y + height / 2 + this.calc(24) }, center, realRotation);
    return { x, y, radius: this.calc(12) };
  }
  // 深度操作节点控件
  get depthWidget() {
    const { center, realRotation, width } = this;
    const { x, y } = transformedCoord({ x: center.x + width / 2 + this.calc(24), y: center.y }, center, realRotation);
    return { x, y, radius: this.calc(12) };
  }
  // 缩放值计算
  calc(num) {
    const { stage } = this;
    return (num / stage.scale) * stage.pixelRatio;
  }
  // 点坐标对应的事件
  pointAction(point) {
    if (!this.visible) return;
    const anchorName = this.pointAnchorName(point);
    if (anchorName) return anchorName;
    if (this.isPointIn(point)) return 'drag';
    else if (this.isPointInRotateWidget(point)) return 'rotate';
    else if (this.isPointInDepthWidget(point)) return 'depth';
    return;
  }
  // 设置对应事件的指针样式
  setCursor(name) {
    const { stage, realRotation } = this;
    if (!name) return;
    if (name === 'drag') stage.setTitle(), stage.setCursor();
    else if (name === 'rotate') stage.setCursor('crosshair');
    else if (name === 'depth') {
      stage.setCursor('pointer');
      stage.setTitle(`点击${stage.depthOperation ? '取消' : '进入'}组内图层编辑状态`);
    } else {
      const index = pointsName.indexOf(name);
      const cIndex = cPointsName.indexOf(name);
      // 将360度按22.5度分块，计算当前角度在哪一块角度区间
      const piece = Math.floor(realRotation / 22.5);
      if (index >= 0) {
        const cursorIndex = (index % 2) * 2 + Math.floor((piece + 1) / 2);
        stage.setCursor(cursorArr[(cursorIndex + 3) % 4]);
      } else if (cIndex >= 0) {
        const cursorIndex = (cIndex % 2) * 2 + Math.floor((piece + 1) / 2);
        stage.setCursor(cursorArr[cursorIndex % 4]);
      }
    }
  }
  // 转换器事件
  action(name, e) {
    if (!name || name === 'drag') return;
    else if (name === 'rotate') this.onRotateStart(e);
    else if (name === 'depth') this.stage.setDepthOperation(!this.stage.depthOperation);
    else {
      const index = pointsName.indexOf(name);
      const cIndex = cPointsName.indexOf(name);
      if (index >= 0 || cIndex >= 0) this.onResizeStart(e, name);
    }
  }
  // 判断点是否在转换器中
  isPointIn(point) {
    if (!this.visible) return false;
    const { stage, nodes, edges } = this;
    // 单个节点 且 当前鼠标悬停位置的节点非画布节点 不做处理
    if (nodes.length === 1 && stage.hoverNode !== stage) return false;
    const ctx = Stage.canvas.getContext();
    const path = new Path2D();
    edges.forEach(({ o, d }) => (path.lineTo(o.x, o.y), path.lineTo(d.x, d.y)));
    path.closePath();
    return ctx.isPointInPath(path, point.x, point.y);
  }
  // 判断点是否在旋转控件中
  isPointInRotateWidget(point) {
    if (!this.visible) return false;
    if (!this.rotateVisible) return false;
    const { x, y, radius } = this.rotateWidget;
    return isPointInsideCircle(point, { x, y }, radius);
  }
  // 判断点是否在深度操作节点控件中
  isPointInDepthWidget(point) {
    if (!this.visible) return false;
    const { nodes } = this;
    if (nodes.length !== 1) return false;
    // 节点为编组 且没有父级编组节点 才有深度操作节点控件
    if (!(nodes[0].className === 'Group' && !nodes[0].topGroup)) return false;
    const { x, y, radius } = this.depthWidget;
    return isPointInsideCircle(point, { x, y }, radius);
  }
  // 获取点坐标对应的锚点名
  pointAnchorName(point) {
    if (!this.visible) return;
    if (!this.resizeVisible) return false;
    const { points, nodes } = this;
    for (const index in points) {
      const { x, y } = points[index];
      if (isPointInsideCircle(point, { x, y }, this.calc(4.5))) return pointsName[index];
    }
    // 多个节点没有边中心锚点
    if (nodes.length !== 1) return;
    const { className } = nodes[0];
    // 图片 文本 节点才有边中心锚点
    if (!['Image', 'Text'].includes(className)) return;
    const { cPoints } = this;
    let name;
    for (const index in cPoints) {
      const { x, y } = cPoints[index];
      if (isPointInsideCircle(point, { x, y }, this.calc(4.5))) name = cPointsName[index];
    }
    // 文本 节点没有上下边中心锚点
    if (className === 'Text' && ['top-center', 'bottom-center'].includes(name)) return;
    return name;
  }
  // 更新转换器位置
  updateOffset(offset = { x: 0, y: 0 }) {
    this.points.forEach((point) => ((point.x += offset.x), (point.y += offset.y)));
  }
  // 转换器状态恢复
  recover(config) {
    if (config.nodes) this.nodes = config.nodes; // 转换节点
    if (config.points) this.points = config.points; // 点集
    if (isNumber(config.rotation)) this.rotation = config.rotation; // 转换器的旋转角度
  }
  // 绘制主要内容
  drawMain(ctx) {
    const { nodes, resizeVisible, rotateVisible } = this;
    const { gridCoord } = this.stage;
    // 多个节点时才绘制轮廓
    if (nodes.length > 1) {
      const { edges } = this;
      draw.outline(ctx, edges, { lineWidth: this.calc(1), lineDash: [this.calc(3), this.calc(2)] });
    }
    // 单个节点
    if (nodes.length === 1) {
      const { cPoints } = this;
      const { className } = nodes[0];
      // 图片 文本 节点才需要绘制边中心锚点
      if (['Image', 'Text'].includes(className)) {
        cPoints.forEach((point, index) => {
          if (className === 'Text' && [0, 2].includes(index)) return;
          const isFill = this.pointAnchorName(gridCoord) === cPointsName[index];
          draw.arc(ctx, { x: point.x, y: point.y, radius: this.calc(4), color: 'white' });
          draw.arc(ctx, { x: point.x, y: point.y, radius: this.calc(4), lineWidth: this.calc(1), isFill });
        });
      }
      // 节点为编组 且没有父级编组节点 绘制深度操作节点控件
      if (className === 'Group' && !nodes[0].topGroup) {
        const { depthOperation } = this.stage;
        const { x, y, radius } = this.depthWidget;
        draw.arc(ctx, { x, y, radius, color: 'white' });
        draw.arc(ctx, { x, y, radius, isFill: depthOperation, lineWidth: this.calc(1) });
        try {
          draw.image(ctx, {
            image: depthOperation ? this.depthImage : this.depthImageTheme,
            x: x - this.calc(8),
            y: y - this.calc(8),
            width: this.calc(16),
            height: this.calc(16),
          });
        } catch (error) {}
      }
    }
    // 绘制顶点锚点
    if (resizeVisible) {
      const { points } = this;
      points.forEach((point, index) => {
        const isFill = this.pointAnchorName(gridCoord) === pointsName[index];
        draw.arc(ctx, { x: point.x, y: point.y, radius: this.calc(4), color: 'white' });
        draw.arc(ctx, { x: point.x, y: point.y, radius: this.calc(4), lineWidth: this.calc(1), isFill });
      });
    }
    // 绘制旋转控件
    if (rotateVisible) {
      const { rotateWidget, rotating } = this;
      const { x, y, radius } = rotateWidget;
      draw.arc(ctx, { x, y, radius });
      try {
        draw.image(ctx, {
          image: this.rotateImage,
          x: x - this.calc(6),
          y: y - this.calc(6),
          width: this.calc(12),
          height: this.calc(12),
        });
      } catch (error) {}
      // 旋转中展示旋转角度
      if (rotating) {
        const { showRotation } = this;
        const text = `${showRotation}°`;
        const width = this.calc(text.length * 6 + 12);
        const height = this.calc(24);
        const finalX = x - width / 2;
        const finalY = y + this.calc(20);
        draw.roundRect(ctx, { x: finalX, y: finalY, width, height, color: 'rgba(0,0,0,0.8)', radius: this.calc(6) });
        const textX = finalX + width / 2;
        const textY = finalY + height / 2;
        draw.text(ctx, { text, x: textX, y: textY, color: 'white', fontSize: this.calc(12), align: 'center' });
      }
    }
  }
  /**
   * Methods
   */
  /**
   * 绘制
   */
  draw(ctx) {
    if (!this.visible) return;
    // 保存当前状态
    ctx.save();
    // 绘制主要内容
    this.drawMain(ctx);
    // 恢复被保存的状态
    ctx.restore();
  }
  /**
   * 获取转换节点
   */
  getNodes() {
    return this.nodes;
  }
  /**
   * 设置转换节点
   * @param {any[]} nodes
   */
  setNodes(nodes = this.nodes, isDraw = true) {
    // 转换节点中有相同的最顶层父级编组，清空转换器节点
    const topGroups = nodes.map((i) => i.topGroup).filter(Boolean);
    if ([...new Set(topGroups)].length !== topGroups.length) nodes = [];
    this.nodes = nodes;
    this.rotation = 0;
    this.points = [];
    // 单个转换节点
    if (nodes.length === 1) this.points = nodes[0].gridRect.points;
    // 多个转换节点
    else if (nodes.length > 1) {
      // 获取多个转换节点在网格坐标系中的最小外接矩形
      const nodesMinEnclosingRect = getNodesMinEnclosingRect(nodes, true);
      if (nodesMinEnclosingRect) {
        const { left, top, width, height } = nodesMinEnclosingRect;
        const { points } = getRealRect(left, top, width, height);
        this.points = points;
      }
    }
    // 重绘
    if (isDraw) this.stage.draw();
  }
  /**
   * 设置/获取旋转角度
   * @param {number} rotation
   */
  rotate(rotation, isDraw = true) {
    if (getType(rotation) !== 'Number') return this.rotation;
    const diffRotation = normalizeRotation(rotation - this.rotation);
    this.rotation = normalizeRotation(rotation);
    const { nodes, points, center } = this;
    // 更新转换节点位置
    nodes.forEach((node) => {
      // 节点中心点坐标
      const nodeCenter = node.gridRect.center;
      // 计算节点在旋转后的中心点坐标
      const transformedCenter = transformedCoord(nodeCenter, center, diffRotation);
      // 计算节点旋转缩放后位置偏移量
      const offset = { x: transformedCenter.x - nodeCenter.x, y: transformedCenter.y - nodeCenter.y };
      node.x += offset.x;
      node.y += offset.y;
      node.rotation = normalizeRotation(node.rotation + diffRotation);
      node.updateParent();
      node.emit('transform');
    });
    // 更新转换器顶点位置
    points.forEach((point) => {
      const { x, y } = transformedCoord(point, center, diffRotation);
      point.x = x;
      point.y = y;
    });
    // 重绘
    if (isDraw) this.stage.draw();
  }
  /**
   * 更新转换器
   */
  update(node) {
    if (!this.visible || this.nodes.length > 1) return;
    if (!node || !node.visible || node.locked) this.setNodes(this.stage.draggableNodes);
    else if (this.nodes.some((i) => i === node || node.parentArr.includes(i))) this.setNodes();
  }
}
